import imghdr
import os
import logging
import time

from pptx import Presentation
import comtypes.client
from lxml import etree
from qfluentwidgets import Dialog

# 配置日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")


def ppt_to_images(ppt_path, output_path):
    """
    将PPT转换为图片，每张幻灯片保存为JPG格式。

    :param ppt_path: 输入PPT文件路径
    :param output_dir: 输出图片目录
    """
    # 校验并创建输出路径
    # 在 output_path 中添加 'sub_dir' 目录
    img_directory = os.path.join(output_path, "img")
    output_path = os.path.join(img_directory, os.path.basename(ppt_path))
    # 如果目录不存在，则创建该目录
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    if not os.path.exists(ppt_path):
        logging.error(f"PPT文件不存在: {ppt_path}")
        return

    try:
        powerpoint = comtypes.client.CreateObject("Powerpoint.Application")
        powerpoint.Visible = 1  # 使PowerPoint窗口可见

        presentation = powerpoint.Presentations.Open(ppt_path)

        for i, slide in enumerate(presentation.Slides):
            slide_file = os.path.join(output_path, f"slide_{i + 1}.jpg")
            slide.Export(slide_file, "JPG")
            logging.info(f"已保存: {slide_file}")

        presentation.Close()
        powerpoint.Quit()
        time.sleep(1)
    except Exception as e:
        logging.error(f"PPT 转图片失败: {e}")
        w = Dialog("PPT 转图片失败", f"请关闭PowerPoint后重新尝试\n具体原因如下:\n{e}")
        if w.exec():
            print('确认')
        else:
            print('取消')


def replace_text_and_collect_images(shape, text_replacements, pic_replacements):
    """
    递归替换文本，并收集待替换的图片信息。

    :param shape: 当前形状对象
    :param text_replacements: 文本替换字典 {旧文本: 新文本}
    :param pic_replacements: 存储待替换图片的列表
    """
    if shape.has_text_frame:
        for paragraph in shape.text_frame.paragraphs:
            for run in paragraph.runs:
                for old_text, new_text in text_replacements.items():
                    if old_text in run.text:
                        run.text = run.text.replace(old_text, new_text)

    if shape.shape_type == 13 and shape.name.startswith("img"):  # 图片类型
        pic_replacements.append({
            "left": shape.left,
            "top": shape.top,
            "width": shape.width,
            "height": shape.height,
            "element": shape._element,  # 获取XML元素，确保层级
            "parent": shape._element.getparent()  # 获取XML父级，保证替换时放置正确位置
        })
    elif shape.shape_type == 6:  # 组类型
        for sub_shape in shape.shapes:
            replace_text_and_collect_images(sub_shape, text_replacements, pic_replacements)


def create_main_image(params, output_path):
    """
    替换PPT模板中的文本和图片，并保持层级顺序。

    :param params: 包含路径和文本替换信息的字典
    :param output_path: 结果PPT输出目录
    """
    template_path = params['template_path']
    main_images_path = params['image_path']

    if not os.path.exists(template_path):
        logging.error(f"模板PPT不存在: {template_path}")
        return None
    if not os.path.exists(main_images_path):
        logging.error(f"图片文件夹不存在: {main_images_path}")
        return None

    old_text_list = [t for t in params['old_text'].split(",") if t]
    new_text_list = [t for t in params['new_text'].split(",") if t]

    if len(old_text_list) != len(new_text_list):
        logging.error("文本替换项数量不匹配")
        return None

    text_replacements = dict(zip(old_text_list, new_text_list))
    # 校验并获取最终输出全文件名
    # 在 output_path 中添加 'sub_dir' 目录
    pptx_directory = os.path.join(output_path, "pptx")

    # 如果目录不存在，则创建该目录
    if not os.path.exists(pptx_directory):
        os.makedirs(pptx_directory)

    # 将 template_path 的文件名添加到 'pptx' 目录
    output_path = os.path.join(pptx_directory, os.path.basename(template_path))

    prs = Presentation(template_path)
    logging.info(f"加载模板: {template_path}")

    for slide in prs.slides:
        pic_replacements = []
        for shape in slide.shapes:
            replace_text_and_collect_images(shape, text_replacements, pic_replacements)

        images_list = get_image_files(main_images_path)  # 确保文件有序
        if len(pic_replacements) > len(images_list):
            logging.error("图片数量不足")
            return None

        # 替换图片时，保持原层级
        for idx, replacement in enumerate(pic_replacements):
            # 删除旧图片
            parent_element = replacement["parent"]
            parent_element.remove(replacement["element"])

            # 插入新图片
            new_image = slide.shapes.add_picture(
                os.path.join(main_images_path, images_list[idx]),
                replacement["left"],
                replacement["top"],
                replacement["width"],
                replacement["height"]
            )

            # 调整XML层级，使新图片保持在原来的层级
            new_element = new_image._element
            parent_element.append(new_element)  # 把新图片插入到原来的位置
    # 确保路径存在
    output_dir = os.path.dirname(output_path)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    prs.save(output_path)
    logging.info(f"文件已保存: {output_path}")
    return output_path


def get_image_files(main_images_path):
    if not os.path.exists(main_images_path):
        logging.error(f"图片文件夹不存在: {main_images_path}")
        return None

    image_files = []

    # 遍历文件夹中的所有文件
    for file_name in os.listdir(main_images_path):
        file_path = os.path.join(main_images_path, file_name)

        # 检查是否是文件且是图片文件
        if os.path.isfile(file_path) and imghdr.what(file_path):
            image_files.append(file_name)

    return image_files
